package com.kitking.surfacevie;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import androidx.annotation.LongDef;

import com.kk.acceleratedraw.BitmapUtil;
import com.kk.acceleratedraw.PenInfo;
import com.kk.acceleratedraw.PenInfo;
import com.kk.acceleratedraw.model.TouchInfo;

import org.apache.commons.math3.fitting.WeightedObservedPoints;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static java.lang.Math.log;
import static java.lang.Math.max;

public class DrawSurFaceView extends SurfaceView implements SurfaceHolder.Callback {

    /**
     * 是否处于绘制状态
     */
    private boolean mIsDrawing;
    /**
     * 帮助类
     */
    private SurfaceHolder mHolder;
    /**
     * 画布
     */
    private Canvas mCanvas;
    /**
     * 路径
     */
    private Path mPath;
    /**
     * 画笔
     */
    private Paint mPaint;
    private Paint mPaintp;
    private Paint mPaintext;
    /**
     * 画布
     */
    private Bitmap backgroundBitmap;
    private Bitmap lineBitmap;

    private final int limitLen = 0;
    private final int maxTime = 500;
    private final int minTime = 2;
    private final float speedV = (float) 0.7;

    WeightedObservedPoints points = new WeightedObservedPoints();

    CoordinateStorage Pstorage = new CoordinateStorage();
    /**
     * 粉笔bitmap
     */
    public Bitmap ChalkPenBitmap = Bitmap.createBitmap(1920, 1080, Bitmap.Config.ARGB_8888);

    private static TouchInfo touchInfo[] = new TouchInfo[10];

    static {
        for (int i = 0; i < touchInfo.length; i++) {
            touchInfo[i] = new TouchInfo();
        }
    }

    private boolean isFirstRun = true;

    private final Map<Integer, MoreFinger> moreFingerList = new LinkedHashMap<>();

    private Canvas backgroundCanvas;
    private Canvas lineCanvas;

    public DrawSurFaceView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public DrawSurFaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public DrawSurFaceView(Context context) {
        super(context);
    }
    float mLastX;
    float mLastY;

    //橡皮擦大小
    public final static int RUBBER_MIN_SIZE = 15;
    public final static int RUBBER_MIDDLE_SIZE = 35;
    public final static int RUBBER_MAX_SIZE = 65;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                mLastX = event.getX();
                mLastY  = event.getY();
                Pstorage.addPoint(mLastX,mLastY,System.currentTimeMillis());
                mPaintp.setColor(0xFFFFFFFF);
                break;
            case MotionEvent.ACTION_MOVE:
                float cX = event.getX();
                float cY = event.getY();
                Pstorage.addPoint(cX,cY,System.currentTimeMillis());
                mPath.lineTo(cX,cY);
                mLastX = cX;
                mLastY = cY;
                lineCanvas.drawCircle(mLastX, mLastY, 3, mPaintp);

                break;
            case MotionEvent.ACTION_POINTER_UP://还有其他手指在屏幕
            case MotionEvent.ACTION_UP:

                draw();

                int psize = Pstorage.getSize();
                extendPoint2(Pstorage,psize);
                if (psize > 2){
                    extendPoint(Pstorage,psize);
                }else {

                    Log.d("TAG", "onTouchEvent: number is less 3");
                }
                Pstorage.clear();
                break;
        }

        return true;
    }

    private void extendPoint(CoordinateStorage storage, int psize) {
        CustomPoint lastCustomPoint = null;

        int totaltime = 0;
        float totallen = 0;
        float endx = 0;
        float endy = 0;
        int npoint = 3;
        float spdoffset = (float) 0.5;

        Log.d("TAG", "==================================");
        Log.d("TAG", "extendPoint: psize: " + psize);
        for (int i = psize; i > 0; i--) {
            CustomPoint customPoint = storage.getCoordinates().get(i-1);
            //1.recording last point parameter
            if (lastCustomPoint == null){
                lastCustomPoint = customPoint;
                endx = lastCustomPoint.getX();
                endy = lastCustomPoint.getY();
                continue;
            }
            //2.calculating  last 3 points spend total times and total length
            float x1 = lastCustomPoint.getX();
            float y1 = lastCustomPoint.getY();
            float x2 = customPoint.getX();
            float y2 = customPoint.getY();
            int times = (int) (lastCustomPoint.getTimestamp() - customPoint.getTimestamp());
            totaltime = totaltime + times;
            float len = (float) Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
            totallen = totallen + len;

            //3. if having close distance point, ignore it
            if (npoint != 0){
                if (len/times < speedV){
                    Log.d("TAG", "extendPoint: len/times: " + len/times + " it's less 1");
                    continue;
                }
                npoint = npoint -1;
                continue;
            }
            Log.d("TAG", "extendPoint: totaltime: " + totaltime + " totallen: " + totallen);
            //4.Is deciding the total times and total length reasonable
            if (totaltime > minTime && totaltime < maxTime ){
                if (len > limitLen){
                    mPath.reset(); // 重置路径
                    mPath.moveTo(mLastX,mLastY);
                    float spd = (float)(SpeedDecayCurve(totallen, totaltime)) + spdoffset ;
                    float x = endx + (endx - x2) * spd;
                    float y = endy + (endy - y2) * spd;
                    mPath.lineTo(x,y);
                    mPaintext.setColor(0xFF0000FF);
                    lineCanvas.drawPath(mPath, mPaintext);
                    draw();
                    Log.d("TAG", "==========EXTEND========= spd: " + spd);
                }else {
                    continue;
                }
                break;
            }else if (totaltime > maxTime){
                break;
            }
            lastCustomPoint = customPoint;
        }
    }

    private void extendPoint2(CoordinateStorage storage, int psize) {
        CustomPoint lastCustomPoint = null;

        int totaltime = 0;
        float totallen = 0;
        float endx = 0;
        float endy = 0;
//        int maxpoint = 13;

        Log.d("TAG", "==================================");
        Log.d("TAG", "extendPoint2: psize: " + psize);
        if (psize < 2){
            Log.i("TAG", "extendPoint2: psize is less 2");
            return;
        }
        for (int i = psize; i > 0; i--) {
//            if (i+maxpoint <= psize){
//                break;
//            }
            CustomPoint customPoint = storage.getCoordinates().get(i-1);
            //1.recording last point parameter
            if (lastCustomPoint == null){
                lastCustomPoint = customPoint;
                endx = lastCustomPoint.getX();
                endy = lastCustomPoint.getY();
                continue;
            }
            //2.calculating  last 3 points spend total times and total length
            float x1 = lastCustomPoint.getX();
            float y1 = lastCustomPoint.getY();
            float x2 = customPoint.getX();
            float y2 = customPoint.getY();
            int times = (int) (lastCustomPoint.getTimestamp() - customPoint.getTimestamp());
            float len = (float) Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
            Log.d("TAG", "extendPoint2: totaltime: " + (totaltime+times) + " totallen: " + (totallen+len) +" i: " + i);
            if (totaltime+times > maxTime){
                if (totallen > limitLen){
                    mPath.reset(); // 重置路径
                    mPath.moveTo(mLastX,mLastY);
                    float spd = (float)(SpeedDecayCurve(totallen, totaltime)) ;
                    float x =endx + (endx - x1) * spd;
                    float y = endy + (endy - y1) * spd;
                    mPath.lineTo(x,y);
                    mPaintext.setColor(0xFF00FF00);
                    lineCanvas.drawPath(mPath, mPaintext);
                    draw();
                    Log.d("TAG", "==========EXTEND0========= spd: " + spd);
                    break;
                }
            }else {
                if (i == 1){
                    totallen = totallen + len;
                    totaltime = totaltime + times;
                    if (totallen > limitLen){
                        mPath.reset(); // 重置路径
                        mPath.moveTo(mLastX,mLastY);
                        float spd = (float)(SpeedDecayCurve(totallen, totaltime)) ;
                        float x =endx + (endx - x2) * spd;
                        float y = endy + (endy - y2) * spd;
                        mPath.lineTo(x,y);
                        mPaintext.setColor(0xFF00FF00);
                        lineCanvas.drawPath(mPath, mPaintext);
                        draw();
                        Log.d("TAG", "==========EXTEND1========= spd: " + spd);
                        break;
                    }
                }
            }
            totaltime = totaltime + times;
            totallen = totallen + len;
            lastCustomPoint = customPoint;
        }
    }

    private double SpeedDecayCurve(float length, int time){
        return Math.log1p(length/time); // y = ln(1 + x) ，Suppress the effect of the maximum value
    }

    private void initView() {

        mHolder = getHolder();
        mHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);

        mPath = new Path();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setDither(true);
        mPaint.setColor(0x35FFFFFF);
        mPaint.setStrokeWidth(12f);

        mPaintp = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintp.setStyle(Paint.Style.STROKE);
        mPaintp.setColor(0xFFFFFFFF);
        mPaintp.setStrokeWidth(12f);

        mPaintext = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintext.setStyle(Paint.Style.STROKE);
        mPaintext.setColor(0xFF0000FF);
        mPaintext.setStrokeWidth(12f);

        lineBitmap = Bitmap.createBitmap(3840, 2160, Bitmap.Config.ARGB_8888);
        lineCanvas = new Canvas(lineBitmap);

        backgroundBitmap = Bitmap.createBitmap(3840, 2160, Bitmap.Config.ARGB_8888);
        backgroundCanvas = new Canvas(backgroundBitmap);
        backgroundCanvas.drawColor(Color.parseColor("#FF0A392A"));
        //DpApi.dpSetEraserBackground(BitmapUtil.bitmapToC(backgroundBitmap));
        //DpApi.dpSetEraserBackground(backgroundBitmap);
//        mPath.reset(); // 重置路径
//        mPath.moveTo(400,400);
//        mPath.lineTo(600,600);
//        mPath.lineTo(700,600);
//        lineCanvas.drawPath(mPath, mPaint);
    }


    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {

    }


    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        draw();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        mIsDrawing = false;
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockHardwareCanvas();
            mCanvas.drawBitmap(backgroundBitmap, 0, 0, null);
            mCanvas.drawBitmap(lineBitmap, 0, 0, null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }


    public void eraserType() {
        MainActivity.PenStatus = -1;
    }

    static class MoreFinger {
        Path path;
        float mLastX;
        float mLastY;
        int id;

        public MoreFinger(Path path, float mLastX, float mLastY, int id) {
            this.path = path;
            this.mLastX = mLastX;
            this.mLastY = mLastY;
            this.id = id;
        }
    }

    /*
     * 触摸点落下事件处理函数
     */
    void kkDrawDown(float xStart, float yStart, int Ptid) {
        touchInfo[Ptid].lastLineWidth = mPaint.getStrokeWidth();
        touchInfo[Ptid].lastMs = System.currentTimeMillis();

        touchInfo[Ptid].startPoint.x = xStart;
        touchInfo[Ptid].startPoint.y = yStart;
    }

    void kkDrawOval(float x, float y, float delta_x, float delta_y) {

        float dst_left = x - delta_x;
        float dst_top = y - delta_y;
        float dst_right = x + delta_x;
        float dst_bottom = y + delta_y;

        RectF rect = new RectF();
        rect.set(dst_left, dst_top, dst_right, dst_bottom);
        PointF ptCenter = new PointF();
        ptCenter.x = rect.centerX();
        ptCenter.y = rect.centerY();

        Matrix matrix = new Matrix();
        matrix.postRotate(40, ptCenter.x, ptCenter.y);

        Path path = new Path();
        path.addOval(rect, Path.Direction.CCW);
        path.transform(matrix);

        lineCanvas.drawPath(path, mPaint);
    }

    void kkDrawCircle(float x, float y, float radius) {

//        Path path = new Path();
//        path.addCircle(x, y, radius, Path.Direction.CW);

        lineCanvas.drawCircle(x, y, radius, mPaint);

//        lineCanvas.drawPath(path, mPaint);
    }

    void kkDrawChalk(float x, float y, float delta_x, float delta_y) {

        float dst_left = x - delta_x;
        float dst_top = y - delta_y;
        float dst_right = x + delta_x;
        float dst_bottom = y + delta_y;

        Rect src=new Rect();
        src.set(0, 0, 40, 38);
        RectF dst=new RectF();
        dst.set(dst_left, dst_top, dst_right, dst_bottom);
        PointF ptCenter=new PointF();
        ptCenter.x = dst.centerX();
        ptCenter.y = dst.centerY();

        //计算区域
        Matrix matrix = new Matrix();
        matrix.setRotate(40, ptCenter.x, ptCenter.y);

        //绘制粉笔
        lineCanvas.save();
        lineCanvas.translate(ptCenter.x,ptCenter.y);
        lineCanvas.rotate(40);
        dst.offset(-ptCenter.x, -ptCenter.y);
        lineCanvas.drawBitmap(ChalkPenBitmap, src, dst, mPaint);
        lineCanvas.restore();
    }

    /**
     * 绘制新增加的线
     */
    private void drawAddPath() {
        lineCanvas.drawPath(getmPath(), mPaint);
    }

    public Path getmPath() {
        return mPath;
    }

    public void setmPath(Path mPath) {
        this.mPath = mPath;
    }
}
